#include <stdio.h>
#include <windows.h>
#include <Wininet.h>
#include <Shlobj.h>

// ** 05/14/2012

static HANDLE h_ppstream;
static HANDLE h_psnetwork_dll;
static HANDLE h_powerplayer_dll;
static HANDLE h_powerlist_dll;
static HANDLE h_vodnet_dll;

typedef struct _PatchEntry PatchEntry, *PatchEntryPtr;
typedef struct _PatchEntry2 PatchEntry2, *PatchEntry2Ptr;
typedef int (*PatchEntry_FuncPtr)(PatchEntry2Ptr pe, unsigned long addr, unsigned long seg_addr, unsigned long seg_size);

struct _PatchEntry {
	const char *s;
	const char *r;
	int sz;
};

struct _PatchEntry2 {
	const char *s;
	int s_sz;
	int s_off;

	const char *r;
	int r_sz;
	int r_off;

	int v_off;
	int v_val;

	PatchEntry_FuncPtr callback;
	void *callback_ctx;
};

static PatchEntry *g_pps;
static PatchEntry _g_pps[] = {
	{"\x00""version\x00", "\x00""verxion\x00", 9},
	{"ppslogin2", "ppsloginx", 9},
	{"ppslogin3", "ppsloginx", 9},
	{0, 0, 0}
};

static PatchEntry *pes;
static PatchEntry _pes[] = {
	{"\x00""nosa=\x00", "\x00""noxx=\x00", 7},
    {"\x00""Bl\x00", "\x00""Bx\x00", 4},
	{"\x00""DL\x00", "\x00""TM\x00", 4},
    {"\x00""Vlevel\x00", "\x00""Vxevel\x00", 8},
    {"\x00""Popt\x00", "\x00""Pxpt\x00", 6},
    {"\x00""Vopt\x00", "\x00""Vxpt\x00", 6},
    {"\x00""Vip\x00", "\x00""Vxp\x00", 5},
	{0, 0, 0}
};

static PatchEntry *pep;
static PatchEntry _pep[] = {
	{"\x00""ads\x00", "\x00""adx\x00", 5},
	{"ads.", "axs.", 4},
	{"msg.", "mxg.", 4},
	{"areainfo.", "areaxxxx.", 9},
	{"player_stat", "player_xxxx", 11},
	{"adpopflash", "xdpopflash", 10},
	{"_ads_", "_axs_", 5},
	{0, 0, 0}
};


int func_patch_cb(PatchEntry2Ptr pe, unsigned long addr, unsigned long seg_addr, unsigned long seg_size);

int __stdcall vobnet_upload_hook(void *buf, int len, int ipv4, int port, void *msg);
static PatchEntry2 vobnet_upload[] = {
	{"\x00""DataRequest\x00", 13, 1, 0, 0, 0, -1, 0, &func_patch_cb, &vobnet_upload_hook},
	{0}
};

void __cdecl powerplayer_vip_hook(void *a0, void *a1, wchar_t *login_str);
static PatchEntry2 powerplayer_vip[] = {
	{"\x00\x00\xEA\x81\xA8\x52\x7B\x76\x55\x5F\xDE\x56\x0D\x59\xE1\x4F\x6F\x60\x3A\x00\x25\x00\x73\x00\x00\x00", 26, 2, 0, 0, 0, -1, 0, &func_patch_cb, &powerplayer_vip_hook},
	{0}
};

void patch_powerplayer();
void patch_powerlist();
void patch_vodnet();
int patch_refpsh(void *img_base, PatchEntry2Ptr pe2);
void add_unicode(PatchEntryPtr *pdst, PatchEntryPtr src, int src_sz);
void patch_ppstream();
void kill_ppsap();
void enable_vip();
void set_app_data_path();

char g_app_data_path[MAX_PATH];

static int cmain()
{
	set_app_data_path();

	h_ppstream = GetModuleHandle(0);
	h_powerplayer_dll = LoadLibrary("PowerPlayer.dll");
	h_powerlist_dll = LoadLibrary("PowerList.ocx");
	h_vodnet_dll = LoadLibrary("Vodnet.dll");
	h_psnetwork_dll = GetModuleHandle("PSNetwork");

	add_unicode(&g_pps, _g_pps, sizeof(_g_pps) / sizeof(_g_pps[0]) - 1);
	add_unicode(&pes, _pes, sizeof(_pes) / sizeof(_pes[0]) - 1);
	add_unicode(&pep, _pep, sizeof(_pep) / sizeof(_pep[0]) - 1);

	patch_ppstream();
	patch_powerplayer();
	patch_powerlist();
	patch_vodnet();

	kill_ppsap();
	enable_vip();

	return 0;
}

static void add_unicode(PatchEntryPtr *pdst, PatchEntryPtr src, int src_sz)
{
	char *m;
	int j;
	PatchEntryPtr ppe0, ppe1;

	*pdst = (PatchEntry*)malloc( (src_sz * 2 + 1) * sizeof(PatchEntry) );
	for(ppe0 = src, ppe1 = *pdst; src_sz > 0; ppe0++, ppe1++, src_sz--) {
		*ppe1 = *ppe0;
		
		ppe1++;
		ppe1->sz = ppe0->sz * 2;

		m = (char*)malloc(ppe1->sz);
		memset(m, 0x00, ppe1->sz);
		for(j = ppe0->sz - 1; j >= 0; j--)
			m[j * 2 + 1] = ppe0->s[j];
		ppe1->s = m;

		m = (char*)malloc(ppe1->sz);
		memset(m, 0x00, ppe1->sz);
		for(j = ppe0->sz - 1; j >= 0; j--)
			m[j * 2 + 1] = ppe0->r[j];
		ppe1->r = m;
	}
	memset(ppe1, 0x00, sizeof(*ppe1));

}

static const char* msearch(const char *tar, int tar_len, const char *val, int val_len)
{
	const char *cur = tar;
	const char *end = tar + tar_len;
	int i;

	while(cur + val_len <= end) {
		for(i = 0; i < val_len; i++) {
			if(cur[i] != val[i]) break;
		}

		if(i == val_len) return cur;
		cur++;
	}

	return NULL;
}

static void set_app_data_path()
{
	char buf[MAX_PATH];
	PIDLIST_ABSOLUTE idl = 0;

	g_app_data_path[0] = 0x00;

	if( !GetPrivateProfileString(0, 0, 0, buf, sizeof(buf), "PPStream.ini") && GetLastError() == ERROR_FILE_NOT_FOUND ) {
		SHGetFolderLocation(0, CSIDL_APPDATA, 0, 0, &idl);
		if(idl) {
			SHGetPathFromIDList(idl, buf);
			ILFree(idl);

			strcat(buf, "\\ppStream\\");
			memcpy(g_app_data_path, buf, sizeof(g_app_data_path));
		}
	}

}

static void patch_txt(void *base, PatchEntryPtr pe, const char *section_nz)
{
	PIMAGE_FILE_HEADER ph;
	PIMAGE_OPTIONAL_HEADER oh;
	PIMAGE_SECTION_HEADER sh, csh;
	int i;
	const char *ret;
	PatchEntryPtr ppe;
	DWORD oldprot, oldprot1;
	void *vaddr, *vend, *caddr, *addr = base;
	
	ph = (PIMAGE_FILE_HEADER)((char*)addr + ((PIMAGE_DOS_HEADER)addr)->e_lfanew + 4);
	oh = (PIMAGE_OPTIONAL_HEADER)((char*)ph + sizeof(IMAGE_FILE_HEADER));
	sh = (PIMAGE_SECTION_HEADER)((char*)oh + ph->SizeOfOptionalHeader);

	for(i = 0; i < ph->NumberOfSections; i++) {
		csh = sh + i;
		if( strcmp(section_nz, (const char*)csh->Name) ) continue;

		caddr = (char*)addr + csh->VirtualAddress;
		VirtualProtect(caddr, csh->SizeOfRawData, PAGE_EXECUTE_READWRITE, &oldprot);
		for(ppe = pe; ppe->s; ppe++) {
			vaddr = caddr;
			vend = (char*)caddr + csh->SizeOfRawData;
			for(; (unsigned long)vaddr + (unsigned long)ppe->sz <= (unsigned long)vend; vaddr = (char*)ret + ppe->sz) {
				ret = msearch((char*)vaddr, (unsigned long)vend - (unsigned long)vaddr, ppe->s, ppe->sz);
				if(!ret) break;
				memcpy((void*)ret, ppe->r, ppe->sz);
			}
		}
		VirtualProtect(caddr, csh->SizeOfRawData, oldprot, &oldprot1);
	}
}

static void patch_ppstream()
{
	patch_txt(h_ppstream, g_pps, ".rdata");
}

static void patch_powerplayer()
{
	patch_txt(h_powerplayer_dll, pep, ".rdata");
}

static void patch_powerlist()
{
	patch_txt(h_powerlist_dll, pes, ".rdata");
}

static void patch_vodnet()
{
	int count = 0;
	PatchEntry2 *pe2 = vobnet_upload;
	while(pe2->s) {
		count += patch_refpsh(h_vodnet_dll, pe2);
		pe2++;
	}

	if(count != 2) MessageBox(0, "ERROR: patch_vodnet()", "patch_vodnet()", 0);
}

static int __stdcall vobnet_upload_hook(void* buf, int len, int ipv4, int port, void *msg)
{
	//printf("Hook:[%d][%d] - %d - %s\n", ipv4, port, len, msg);
	return len;
}

static int func_patch_cb(PatchEntry2Ptr pe, unsigned long addr, unsigned long seg_addr, unsigned long seg_size)
{
	unsigned long v;
	unsigned long end;

	addr += 5;
	end = addr + 32;
	for(; addr < end; addr++) {
		if( *(unsigned char*)(addr) != 0xE8 ) continue;
		v = *(unsigned long*)(addr + 1) + addr + 5;
		if(v < seg_addr || v >= seg_addr + seg_size) continue;
		*(unsigned long*)(addr + 1) = (unsigned long)pe->callback_ctx - (addr + 5);
		return 1;
	}

	return 0;
}

static int patch_refpsh(void *img_base, PatchEntry2Ptr pe2)
{
	int count = 0;
	PIMAGE_FILE_HEADER ph;
	PIMAGE_OPTIONAL_HEADER oh;
	PIMAGE_SECTION_HEADER sh, csh;
	int i;
	const char *ret;
	DWORD oldprot, oldprot1;
	void *vend, *caddr, *vaddr, *addr = (void*)img_base;
	char intr[5] = {0x68};

	ph = (PIMAGE_FILE_HEADER)((char*)addr + ((PIMAGE_DOS_HEADER)addr)->e_lfanew + 4);
	oh = (PIMAGE_OPTIONAL_HEADER)((char*)ph + sizeof(IMAGE_FILE_HEADER));
	sh = (PIMAGE_SECTION_HEADER)((char*)oh + ph->SizeOfOptionalHeader);

	for(i = 0; i < ph->NumberOfSections; i++) {
		csh = sh + i;
		if( strcmp(".rdata", (const char*)csh->Name) ) continue;

		caddr = (char*)addr + csh->VirtualAddress;
		ret = msearch((char*)caddr, csh->SizeOfRawData, pe2->s, pe2->s_sz);
		if(ret) break;
	}
	if(!ret) return count;

	*(unsigned long*)(intr + 1) = (unsigned long)(ret + pe2->s_off);
	for(i = 0; i < ph->NumberOfSections; i++) {
		csh = sh + i;
		if( strcmp(".text", (const char*)csh->Name) ) continue;

		caddr = (char*)addr + csh->VirtualAddress;
		VirtualProtect(caddr, csh->SizeOfRawData, PAGE_EXECUTE_READWRITE, &oldprot);
		vaddr = caddr;
		vend = (char*)addr + csh->SizeOfRawData;
		for(; (unsigned long)vaddr + sizeof(intr) <= (unsigned long)vend; vaddr = (char*)ret + sizeof(intr)) {
			ret = msearch((char*)vaddr, (unsigned long)vend - (unsigned long)vaddr, intr, sizeof(intr));
			if(!ret) break;
			if( pe2->v_off >= 0 && (unsigned char)ret[pe2->v_off] != (unsigned char)(unsigned int)pe2->v_val ) continue;
			if( pe2->callback && !pe2->callback(pe2, (unsigned long)ret, (unsigned long)caddr, csh->SizeOfRawData) ) continue;
			if( pe2->r ) memcpy((char*)ret + pe2->r_off, pe2->r, pe2->r_sz);
			count++;
		}
		VirtualProtect(caddr, csh->SizeOfRawData, oldprot, &oldprot1);
	}

	return count;
}

static void kill_ppsap()
{
	DWORD pid = 0;
	HWND hwnd;
	HANDLE ph;
	char buf[MAX_PATH];

	hwnd = FindWindow("\x50\x50\x53\x74\x72\x65\x61\x6D\x20\xCD\xF8\xC2\xE7\xBC\xD3\xCB\xD9\xC6\xF7", 0);
	if(hwnd) {
		GetWindowThreadProcessId(hwnd, &pid);
		if(pid) {
			ph = OpenProcess(PROCESS_TERMINATE, 0, pid);
			if(ph) {
				TerminateProcess(ph, 0);
				CloseHandle(ph);
			}
		}
	}

	memcpy(buf, g_app_data_path, sizeof(buf));
	strcat(buf, "psnetwork.ini");
	WritePrivateProfileString("vodnet", "bgs", "0", buf);
}

static void __cdecl powerplayer_vip_hook(void *a0, void *a1, wchar_t *login_str)
{
	const wchar_t new_login_str[] = L"ret=0;sec=1200;uid=1;level=899999;name=null;day=100;opt=3;reday=100;nick_name=null";
	printf("oldLog: %S", login_str);


	if(!login_str) return;

	if(wcslen(login_str) < wcslen(new_login_str) ) {
		MessageBox(0, "powerplayer_vip_hook error", "error", 0);
		return;
	}

	memcpy(login_str, new_login_str, sizeof(new_login_str));
}

static void enable_vip()
{
	char buf[MAX_PATH];
	int count = 0;
	PatchEntry2 *pe2 = powerplayer_vip;

	InternetSetCookie("http://pps.tv", "ppslogin2", "1;expires=Wed,16-Dec-2020 19:00:00GMT;path=/");
	InternetSetCookie("http://pps.tv", "ppslogin3", "1;expires=Wed,16-Dec-2020 19:00:00GMT;path=/");

	memcpy(buf, g_app_data_path, sizeof(buf));
	strcat(buf, "powerplayer.ini");

	WritePrivateProfileString("verify", "at", "1", buf);
	WritePrivateProfileString("verify", "un", "null", buf);
	WritePrivateProfileString("verify", "pkey", "CF3DDB4DA5FA945C50772A8F774CD132", buf);
	WritePrivateProfileString("verify", "pw", "CF3DDB4DA5FA945C50772A8F774CD132", buf);

	while(pe2->s) {
		count += patch_refpsh(h_powerplayer_dll, pe2);
		pe2++;
	}

	if(count != 1) MessageBox(0, "ERROR: enable_vip() 2", "enable_vip() 2", 0);


}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
	static int init = 0;
	char lib[MAX_PATH];

	switch(fdwReason)
	{ 
		case DLL_PROCESS_ATTACH:
			{
				if(!init) {
					//AllocConsole();
					//freopen("CONOUT$", "wb", stdout);
					init = 1;
					GetModuleFileName(hinstDLL, lib, sizeof(lib));
					LoadLibrary(lib);
					cmain();
				}
			}
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
		case DLL_PROCESS_DETACH:
			break;
	}

	return 1;
}

